package top.bingoz.wanandroid.github

import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.mock.BehaviorDelegate
import retrofit2.mock.MockRetrofit
import retrofit2.mock.NetworkBehavior
import top.bingoz.wanandroid.github.SimpleService.Contributor
import top.bingoz.wanandroid.github.SimpleService.GitHub
import java.io.IOException
import java.util.*
import java.util.concurrent.TimeUnit

object SimpleMockService {
    /**
     * A mock implementation of the [GitHub] API interface.
     */
    internal class MockGitHub(private val delegate: BehaviorDelegate<GitHub>) : GitHub {
        private val ownerRepoContributors: MutableMap<String, MutableMap<String, MutableList<Contributor>>>

        init {
            ownerRepoContributors = LinkedHashMap()

            // Seed some mock data.
            addContributor("square", "retrofit", "John Doe", 12)
            addContributor("square", "retrofit", "Bob Smith", 2)
            addContributor("square", "retrofit", "Big Bird", 40)
            addContributor("square", "picasso", "Proposition Joe", 39)
            addContributor("square", "picasso", "Keiser Soze", 152)
        }

        override fun contributors(owner: String, repo: String): Call<List<Contributor>> {
            var response = emptyList<Contributor>()
            val repoContributors = ownerRepoContributors[owner]
            if (repoContributors != null) {
                val contributors = repoContributors[repo]
                if (contributors != null) {
                    response = contributors
                }
            }
            return delegate.returningResponse(response).contributors(owner, repo)
        }

        fun addContributor(owner: String, repo: String, name: String, contributions: Int) {
            var repoContributors: MutableMap<String, MutableList<Contributor>>? = ownerRepoContributors[owner]
            if (repoContributors == null) {
                repoContributors = LinkedHashMap()
                ownerRepoContributors.put(owner, repoContributors)
            }
            var contributors: MutableList<Contributor>? = repoContributors[repo]
            if (contributors == null) {
                contributors = ArrayList()
                repoContributors[repo] = contributors
            }
            contributors.add(Contributor(name, contributions))
        }
    }

    @Throws(IOException::class)
    @JvmStatic
    fun main(args: Array<String>) {
        // Create a very simple Retrofit adapter which points the Api API.
        val retrofit = Retrofit.Builder()
                .baseUrl(SimpleService.API_URL)
                .build()

        // Create a MockRetrofit object with a NetworkBehavior which manages the fake behavior of calls.
        val behavior = NetworkBehavior.create()
        val mockRetrofit = MockRetrofit.Builder(retrofit)
                .networkBehavior(behavior)
                .build()

        val delegate = mockRetrofit.create(GitHub::class.java)
        val gitHub = MockGitHub(delegate)

        // Query for some contributors for a few repositories.
        printContributors(gitHub, "square", "retrofit")
        printContributors(gitHub, "square", "picasso")

        // Using the mock-only methods, add some additional data.
        println("Adding more mock data...\n")
        gitHub.addContributor("square", "retrofit", "Foo Bar", 61)
        gitHub.addContributor("square", "picasso", "Kit Kat", 53)

        // Reduce the delay to make the next calls complete faster.
        behavior.setDelay(500, TimeUnit.MILLISECONDS)

        // Query for the contributors again so we can see the mock data that was added.
        printContributors(gitHub, "square", "retrofit")
        printContributors(gitHub, "square", "picasso")
    }

    @Throws(IOException::class)
    private fun printContributors(gitHub: GitHub, owner: String, repo: String) {
        println(String.format("== Contributors for %s/%s ==", owner, repo))
        val contributors = gitHub.contributors(owner, repo)
        for (contributor in contributors.execute().body()!!) {
            println(contributor.login + " (" + contributor.contributions + ")")
        }
        println()
    }
}